﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iLangl.Model;
using System.Collections;

namespace iLangl.BusinessModel.DAL.XMLStorage
{
	public class SynchronizationDAL : BaseStorageDAL, ISynchronizationDAL
	{

		public SyncedMutliWaysData SyncObjects()
		{
			SyncedMutliWaysData result = new SyncedMutliWaysData();
			Hashtable companies = new Hashtable();

			//Company sync
			foreach (dtoXmlStorage.CompanyRow cr in Storage.Company)
			{
				foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
				{
					if ((sdr.ObjectType == eEntityType.Company.ToString()) && (sdr.ObjectId == cr.Id))
					{
						TimeSpan syncspan = sdr.SyncTimeStamp;

						if (IsRequiredToSync(syncspan, cr.TimeSpan))
						{
							Company company = null;
							company = (Company)companies[cr.Id];

							if (companies[cr.Id] == null)
							{
								company = DALFacade.Instance.CompanyDAL.ReadCompany(cr.Id);
								companies[cr.Id] = company;
								
							}
							
							String key = GetKey(company);
							result.TimeSpans[key] = cr.TimeSpan;


							result.AddConsumer(key, company, sdr.ConsumerId);
						}
					}
				}
			}

			//Billing Info
			foreach (dtoXmlStorage.BillingInfoRow br in Storage.BillingInfo)
			{
				foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
				{
					if ((sdr.ObjectType == eEntityType.BillingInfo.ToString()) && (sdr.ObjectId == br.Id))
					{
						TimeSpan syncspan = sdr.SyncTimeStamp;

						if (IsRequiredToSync(syncspan, br.TimeSpan))
						{
							BillingInfo bi = DALFacade.Instance.CompanyDAL.ReadBillingInfo(br.Id);

							String biKey = GetKey(bi);
							result.TimeSpans[biKey] = br.TimeSpan;

							if (companies[bi.Company.Id] == null)
							{
								companies[bi.Company.Id] = bi.Company;
							}
							else
							{
								bi.Company = (Company)companies[bi.Company.Id];
							}

							result.AddConsumer(GetKey(bi.Company), bi.Company, sdr.ConsumerId);
						}
					}
				}
			}

			//Contact Info
			foreach (dtoXmlStorage.ContactRow cr in Storage.Contact)
			{
				foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
				{
					if ((sdr.ObjectType == eEntityType.Contact.ToString()) && (sdr.ObjectId ==cr.Id))
					{
						TimeSpan syncpan = sdr.SyncTimeStamp;

						if (IsRequiredToSync(syncpan, cr.TimeSpan))
						{
							Contact contact = DALFacade.Instance.ContactDAL.ReadContact(cr.Id);

							String crKey = GetKey(contact);
							result.TimeSpans[crKey] = cr.TimeSpan;

							if (companies[contact.Company.Id] == null)
							{
								companies[contact.Company.Id] = contact.Company;
							}
							else
							{
								contact.Company = (Company)companies[contact.Company.Id];
							}

							Company company = (Company)companies[contact.Company.Id];
							contact.Company = company;
							company.AddContact(contact);

							result.AddConsumer(GetKey(company),company, sdr.ConsumerId);
						}
					}
				}
			}



			Hashtable projects = new Hashtable();
			//Project
			foreach (dtoXmlStorage.ProjectRow pr in Storage.Project)
			{
				foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
				{
					if ((sdr.ObjectType == eEntityType.Project.ToString()) && (sdr.ObjectId == pr.Id))
					{
						TimeSpan syncspan = sdr.SyncTimeStamp;

						if (IsRequiredToSync(syncspan, pr.TimeSpan))
						{
							Project project = DALFacade.Instance.ProjectDAL.ReadProject(pr.Id);

							String prKey = GetKey(project);

							result.TimeSpans[prKey] = pr.TimeSpan;
							projects[pr.Id] = project;

							result.AddConsumer(prKey, project, sdr.ConsumerId);
						}
					}
				}
			}
			//Translaion
			foreach (dtoXmlStorage.TranslationRow tr in Storage.Translation)
			{
				foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
				{
					if ((sdr.ObjectType == eEntityType.Translation.ToString()) && (sdr.ObjectId == tr.Id))
					{
						TimeSpan syncspan = sdr.SyncTimeStamp;

						if (IsRequiredToSync(syncspan, tr.TimeSpan))
						{
							Translation translation = DALFacade.Instance.TranslationDAL.ReadTranslation(tr.Id);
							String trKey = GetKey(translation);
							result.TimeSpans[trKey] = tr.TimeSpan;

							if (projects[translation.Project.Id] == null)
							{
								projects[translation.Project.Id] = translation.Project;
							}
							else
							{
								translation.Project = (Project)projects[translation.Project.Id];
							}

							Project project = (Project)projects[translation.Project.Id];
							project.AddTranslation(translation);

							result.AddConsumer(GetKey(project), project, sdr.ConsumerId);
						}
					}

				}
			}


			//Content
			foreach (dtoXmlStorage.ContentRow cr in Storage.Content)
			{
				foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
				{
					if ((sdr.ObjectType == eEntityType.Content.ToString()) && (sdr.ObjectId == cr.Id))
					{
						TimeSpan syncspan = sdr.SyncTimeStamp;


						if (IsRequiredToSync(syncspan, cr.TimeSpan))
						{
							ContentEntity content = DALFacade.Instance.ContentDAL.ReadContent(cr.Id);
							String crKey = GetKey(content);
							result.TimeSpans[crKey] = cr.TimeSpan;

							if (projects[content.Project.Id] == null)
							{
								projects[content.Project.Id] = content.Project;
							}
							else
							{

								content.Project = (Project)projects[content.Project.Id];
							}

							Project project = (Project)projects[content.Project.Id];
							project.AddContent(content);

							result.AddConsumer(GetKey(project), project, sdr.ConsumerId);
						}

					}
				}
			}

			//Accumulte data
			IList<Company> lcompanies = new List<Company>();

			foreach (String key in companies.Keys)
			{
				lcompanies.Add((Company)companies[key]);
			}

			IList<Project> lprojects = new List<Project>();

			foreach (String key in projects.Keys)
			{
				lprojects.Add((Project)projects[key]);
			}

			result.Companies = lcompanies.ToArray();
			result.Projects = lprojects.ToArray();

			return result;
		}

		public void MarkAsSynced(SyncedMutliWaysData data)
		{
			TimeSpan ts;
			foreach (Company company in data.Companies)
			{
				String companykey = GetKey(company);
				ts = data.GetTimeSpan(companykey);
				foreach (String subscriberid in data.GetSubscribers(companykey))
				{
					SyncCompany(company, ts, subscriberid);
				}

				if (company.BillingInfo != null)
				{
					String billingKey = GetKey(company.BillingInfo);
					ts = data.GetTimeSpan(billingKey);

					foreach (String subscriberid in data.GetSubscribers(companykey))
					{
						SyncBillingInfo(company.BillingInfo, ts, subscriberid);
					}
				}

				foreach (Contact contact in company.Contacts)
				{
					String contactKey = GetKey(contact);
					ts = data.GetTimeSpan(contactKey);
					foreach (String subscriberid in data.GetSubscribers(contactKey))
					{
						SyncContact(contact, data.GetTimeSpan(contactKey), subscriberid);
					}
				}
			}

			foreach (Project project in data.Projects)
			{
				String projectKey = GetKey(project);
				ts = data.GetTimeSpan(projectKey);

				foreach (String subscriberid in data.GetSubscribers(projectKey))
				{
					SyncProject(project, ts, subscriberid);
				}

				foreach (ContentEntity content in project.Contents)
				{
					String contentKey = GetKey(content);
					ts = data.GetTimeSpan(contentKey);

					foreach (String subscriberid in data.GetSubscribers(contentKey))
					{
						SyncContent(content, ts, subscriberid);
					}
				}

				foreach (Translation translaton in project.Translations)
				{
					String translationKey = GetKey(translaton);
					ts = data.GetTimeSpan(translationKey);
					foreach (String subscriberid in data.GetSubscribers(translationKey))
					{
						SyncTranslation(translaton, ts, subscriberid);
					}
				}
			}
		}



		public void MarkAsSynced(SyncedOneWayData data)
		{
			TimeSpan ts;
			foreach (Company company in data.Companies)
			{
				String companykey = GetKey(company);
				ts = data.GetTimeSpan(companykey);

				SyncCompany(company, ts, data.Subscriber);

				if (company.BillingInfo != null)
				{
					String billingKey = GetKey(company.BillingInfo);
					ts = data.GetTimeSpan(billingKey);

					SyncBillingInfo(company.BillingInfo, ts, data.Subscriber);

				}

				foreach (Contact contact in company.Contacts)
				{
					String contactKey = GetKey(contact);
					ts = data.GetTimeSpan(contactKey);

					SyncContact(contact, data.GetTimeSpan(contactKey), data.Subscriber);

				}
			}

			foreach (Project project in data.Projects)
			{
				if (project == null)
					continue;

				String projectKey = GetKey(project);
				ts = data.GetTimeSpan(projectKey);


				SyncProject(project, ts, data.Subscriber);

				if (project.Contents != null)
					foreach (ContentEntity content in project.Contents)
					{
						String contentKey = GetKey(content);
						ts = data.GetTimeSpan(contentKey);


						SyncContent(content, ts, data.Subscriber);
					}

				if (project.Translations != null)
					foreach (Translation translaton in project.Translations)
					{
						String translationKey = GetKey(translaton);
						ts = data.GetTimeSpan(translationKey);
						SyncTranslation(translaton, ts, data.Subscriber);

					}
			}
		}

		#region Helper Methods
		public void SyncContact(Contact contact, TimeSpan timespan, String subscriberId)
		{
			UpdateDeliveryTime(eEntityType.Contact, contact.Id, timespan, subscriberId);
		}

		public void SyncCompany(Company company, TimeSpan timespan, String subscriberId)
		{
			UpdateDeliveryTime(eEntityType.Company, company.Id, timespan, subscriberId);
		}

		public void SyncBillingInfo(BillingInfo billingInfo, TimeSpan timespan, String subscriberId)
		{
			UpdateDeliveryTime(eEntityType.BillingInfo, billingInfo.Id, timespan, subscriberId);
		}

		public void SyncProject(Project project, TimeSpan timespan, String subscriberId)
		{
			UpdateDeliveryTime(eEntityType.Project, project.Id, timespan, subscriberId);
		}

		public void SyncContent(ContentEntity content, TimeSpan timespan, String subscriberId)
		{
			UpdateDeliveryTime(eEntityType.Content, content.Id, timespan, subscriberId);
		}

		public void SyncTranslation(Translation translation, TimeSpan timespan, String subscriberId)
		{
			UpdateDeliveryTime(eEntityType.Translation, translation.Id, timespan, subscriberId);
		}

		public void UpdateDeliveryTime(eEntityType type, String objectId, TimeSpan time, String subscriberId)
		{
			if (time == TimeSpan.MinValue)
				return;

			dtoXmlStorage.SyncDataRow result = null;

			foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
			{
				if ((sdr.ObjectType == type.ToString()) && (sdr.ObjectId == objectId) && (sdr.ConsumerId == subscriberId))
				{
					result = sdr;
					break;
				}
			}

			bool isNew = (result == null);

			if (isNew)
			{
				result = Storage.SyncData.NewSyncDataRow();
			}

			result.ObjectId = objectId;
			result.ObjectType = type.ToString();
			result.SyncTimeStamp = time;
			result.ConsumerId = subscriberId;

			if (isNew)
			{
				Storage.SyncData.AddSyncDataRow(result);
			}

			AcceptChanges();
		}

		public bool IsRequiredToSync(TimeSpan syncTimeSpam, TimeSpan currentTimeSpan)
		{
			return syncTimeSpam < currentTimeSpan;
		}

		

		public TimeSpan MinTimeSpan
		{
			get
			{
				return new TimeSpan(DateTime.MinValue.Ticks);
			}
		}

		public String GetKey(IDuplex duplexobject)
		{
			return string.Format("{0}_{1}", duplexobject.GetType().ToString(), duplexobject.Id);
		}
		#endregion


		public void AddSubscriber(eEntityType type, string objectId, string subscriberId, bool isNeedToSync)
		{
			if(isNeedToSync)
				UpdateDeliveryTime(type, objectId, MinTimeSpan, subscriberId);
			else
				UpdateDeliveryTime(type, objectId, CurrentTimeSpan, subscriberId);
		}

		public void RemoveSubscriber(eEntityType type, string objectId)
		{
			ArrayList todelete = new ArrayList();

			foreach (dtoXmlStorage.SyncDataRow sdr in Storage.SyncData)
			{
				if ((sdr.ObjectType == type.ToString()) && (sdr.ObjectId == objectId))
				{
					todelete.Add(sdr);
				}
			}

			foreach (dtoXmlStorage.SyncDataRow sdr in todelete)
			{
				Storage.SyncData.Rows.Remove(sdr);
			}

			AcceptChanges();
		}

		public void RemoveSubscriber(eEntityType type, string objectId, string subscriberId)
		{
			dtoXmlStorage.SyncDataRow sdr = Storage.SyncData.FindByConsumerIdObjectTypeObjectId(subscriberId, type.ToString(), objectId);

			if (sdr != null)
			{
				Storage.SyncData.Rows.Remove(sdr);
				AcceptChanges();
			}
		}
	}
}
